
#import "WWThumbWheel.h"
#import "WWThumbWheelCell.h"
#import "WWTCLInterp.h"
#import <ctype.h>

@implementation WWThumbWheelCell


+ initialize { [WWThumbWheelCell setVersion:5]; return self; }
////////////////////////////////////////////////////////
//
- init
{
  [super init];
  controlStringSize = 32;
  controlString = (char *)NXZoneCalloc([self zone], controlStringSize, sizeof(char));
  sprintfControlString = (char *)NXZoneCalloc([self zone], controlStringSize, sizeof(char));
  tclExpressionSize = 32;
  tclExpression = (char *)NXZoneCalloc([self zone], tclExpressionSize, sizeof(char));
  tclVarSize = 32;
  tclVar = (char *)NXZoneCalloc([self zone], tclVarSize, sizeof(char));
  tclCommandSize = 32;
  tclCommand = (char *)NXZoneCalloc([self zone], tclCommandSize, sizeof(char));
  tclCommandDirty = YES;

  return self;
}
//
- awake
{
  [super awake];
  tclCommandDirty = YES;
  return self;
}

- free
{
  if (controlString) { NXZoneFree([self zone], controlString); }
  if (sprintfControlString) { NXZoneFree([self zone], sprintfControlString); }
  if (tclExpression) { NXZoneFree([self zone], tclExpression); }
  if (tclVar) { NXZoneFree([self zone], tclVar); }
  if (tclCommand) { NXZoneFree([self zone], tclCommand); }
  return [super free];
}

//
- awakeFromNib
{
  [visibleMinText setFloatValue:[self visibleMin]];
  [visibleMaxText setFloatValue:[self visibleMax]];
  [absoluteMinText setFloatValue:[self absoluteMin]];
  [absoluteMaxText setFloatValue:[self absoluteMax]];
  [valText setFloatValue:[self floatValue]];
  [relativeValText setFloatValue:[self relativeFloatValue]];
  return self;
}
//
- setVisibleMin:(float)value
{
   [visibleMinText setFloatValue:value];
   return [super setVisibleMin:value];
}
//
- setVisibleMax:(float)value
{
   [visibleMaxText setFloatValue:value];
   return [super setVisibleMax:value];
}
//
- setAbsoluteMin:(float)value
{
   [absoluteMinText setFloatValue:value];
   return [super setAbsoluteMin:value];
}
//
- setAbsoluteMax:(float)value
{
   [absoluteMaxText setFloatValue:value];
   return [super setAbsoluteMax:value];
}

- setStringValue:(const char *)str
{
  char  *theStr;

  [super setStringValue:str];

  // boy, this is kind of a hack - this should just work...
  // use super method to avoid redoing the rest...
  [super setFloatValue:(float)atof(str)];

  theStr = (char *)[self stringValue];
  [visibleMaxText setStringValue:theStr];
  [visibleMinText setStringValue:theStr];
  [absoluteMaxText setStringValue:theStr];
  [absoluteMinText setStringValue:theStr];
  [valText setStringValue:theStr];
  [relativeValText setStringValue:theStr];
  tclCommandDirty = YES;
  return self;
}

- setIntValue:(int)val
{
  int   theVal;


  [super setIntValue:val];
  theVal = [self intValue];
  [visibleMaxText setIntValue:theVal];
  [visibleMinText setIntValue:theVal];
  [absoluteMaxText setIntValue:theVal];
  [absoluteMinText setIntValue:theVal];
  [valText setIntValue:theVal];
  [relativeValText setIntValue:theVal];
  tclCommandDirty = YES;
  return self;
}

- setFloatValue:(float)val
{
  float  theVal;


  [super setFloatValue:val];
  theVal = [self floatValue];
  [visibleMaxText setFloatValue:theVal];
  [visibleMinText setFloatValue:theVal];
  [absoluteMaxText setFloatValue:theVal];
  [absoluteMinText setFloatValue:theVal];
  [valText setFloatValue:theVal];
  [relativeValText setFloatValue:theVal];
  tclCommandDirty = YES;
  return self;
}

- setDoubleValue:(double)val
{
  double  theVal;


  [super setDoubleValue:val];
  theVal = [self doubleValue];
  [visibleMaxText setDoubleValue:theVal];
  [visibleMinText setDoubleValue:theVal];
  [absoluteMaxText setDoubleValue:theVal];
  [absoluteMinText setDoubleValue:theVal];
  [valText setDoubleValue:theVal];
  [relativeValText setDoubleValue:theVal];
  tclCommandDirty = YES;
  return self;
}

- reinitializeControlStringAndTclVarFromZone:(NXZone *)zone
{
  controlString = (char *)NXZoneCalloc(zone, controlStringSize, sizeof(char));
  sprintfControlString = (char *)NXZoneCalloc(zone, controlStringSize, sizeof(char));
  tclExpression = (char *)NXZoneCalloc(zone, tclExpressionSize, sizeof(char));
  tclVar = (char *)NXZoneCalloc(zone, tclVarSize, sizeof(char));
  tclCommand = (char *)NXZoneCalloc(zone, tclCommandSize, sizeof(char));
  return self;
}

- copyFromZone:(NXZone *)zone
{
  id newCopy = [super copyFromZone:zone];


  [newCopy reinitializeControlStringAndTclVarFromZone:zone];
  [newCopy setControlString:controlString];
  [newCopy setTclExpression:tclExpression];
  [newCopy setTclVar:tclVar];
  [newCopy setTclCommand:tclCommand];

  return newCopy;
}

- setInterp:newInterp {  interp = newInterp; return self; }
- interp { return interp; }

- resizeTclCommandForArgVector:(char *)argOrderVector
{
  BOOL    done = NO;


  if (!done && !strcmp("d", argOrderVector))
  {  while (tclCommandSize < (controlStringSize + 32))
     {  if (!tclCommandSize)
	{  tclCommandSize = 32;
           tclCommand = (char *)NXZoneCalloc([self zone], tclCommandSize, sizeof(char));
        }
        else
        {  tclCommandSize *= 2;
           tclCommand = (char *)NXZoneRealloc([self zone], tclCommand, tclCommandSize);
        }
     }
     done = YES;
  }
  if (!done && !strcmp("f", argOrderVector))
  {  while (tclCommandSize < (controlStringSize + 32) )
     {  if (!tclCommandSize)
	{  tclCommandSize = 32;
           tclCommand = (char *)NXZoneCalloc([self zone], tclCommandSize, sizeof(char));
        }
        else
        {  tclCommandSize *= 2;
           tclCommand = (char *)NXZoneRealloc([self zone], tclCommand, tclCommandSize);
        }
     }
     done = YES;
  }
  if (!done && !strcmp("fff", argOrderVector))
  {  while (tclCommandSize  < (controlStringSize + (2 + (3 * 32))))
     {  if (!tclCommandSize)
	{  tclCommandSize = 32;
           tclCommand = (char *)NXZoneCalloc([self zone], tclCommandSize, sizeof(char));
        }
        else
        {  tclCommandSize *= 2;
           tclCommand = (char *)NXZoneRealloc([self zone], tclCommand, tclCommandSize);
        }
     }
     done = YES;
  }

  if (!done && !strcmp("RGB", argOrderVector))
  {  while (tclCommandSize  < (controlStringSize + (2 + (3 * 32))))
     {  if (!tclCommandSize)
	{  tclCommandSize = 32;
           tclCommand = (char *)NXZoneCalloc([self zone], tclCommandSize, sizeof(char));
        }
        else
        {  tclCommandSize *= 2;
           tclCommand = (char *)NXZoneRealloc([self zone], tclCommand, tclCommandSize);
        }
     }
     done = YES;
  }

  if (!done && !strcmp("s", argOrderVector))
  {  const char  *str = [self stringValue]; 
     int   strLen = strlen(str);

     while (tclCommandSize < (controlStringSize + strLen))
     {  if (!tclCommandSize)
	{  tclCommandSize = 32;
           tclCommand = (char *)NXZoneCalloc([self zone], tclCommandSize, sizeof(char));
        }
        else
        {  tclCommandSize *= 2;
           tclCommand = (char *)NXZoneRealloc([self zone], tclCommand, tclCommandSize);
        }
     }
     done = YES;
  }
  // those were the easy ones; now for the permutations...

  return self;
}


- updateTclCommand
{
  char    *controlStringPtr, *sprintfControlStringPtr;
  BOOL    done;
  int     argOrderSize = 32;
  char    *argOrderVector = (char *)NXZoneCalloc([self zone], argOrderSize, sizeof(char));
  int     argOrderOffset = 0;


  if (!controlString)
  {  return nil;
  }

  // we want to grovel over the controlString, 
  // for right now, we really only tend to have the following patterns 
  // in the controlString: 1 %f, 1 %d, 1 %s, %R, %G, %B
  controlStringPtr = controlString;
  sprintfControlStringPtr = sprintfControlString;
  strcpy(sprintfControlString, controlString);
  while (*controlStringPtr)
  {  if (*controlStringPtr == '%')
     {  sprintfControlStringPtr++;  controlStringPtr++;
        if (*controlStringPtr)
        {  switch (*controlStringPtr)
           {  case 'd':	while (argOrderOffset >= argOrderSize)
                        {  argOrderSize *= 2;
                           argOrderVector = (char *)NXZoneRealloc([self zone], argOrderVector, argOrderSize);
                        }
                        argOrderVector[argOrderOffset++] = 'd';
                        sprintfControlStringPtr++; controlStringPtr++;
			break;
              case 'f':	while (argOrderOffset >= argOrderSize)
                        {  argOrderSize *= 2;
                           argOrderVector = (char *)NXZoneRealloc([self zone], argOrderVector, argOrderSize);
                        }
                        argOrderVector[argOrderOffset++] = 'f';
                        sprintfControlStringPtr++; controlStringPtr++;
			break;
              case 's':	while (argOrderOffset >= argOrderSize)
                        {  argOrderSize *= 2;
                           argOrderVector = (char *)NXZoneRealloc([self zone], argOrderVector, argOrderSize);
                        }
                        argOrderVector[argOrderOffset++] = 's';
                        sprintfControlStringPtr++; controlStringPtr++;
			break;
              case 'R':	while (argOrderOffset >= argOrderSize)
                        {  argOrderSize *= 2;
                           argOrderVector = (char *)NXZoneRealloc([self zone], argOrderVector, argOrderSize);
                        }
                        argOrderVector[argOrderOffset++] = 'R';
                        *sprintfControlStringPtr++ = 'f'; controlStringPtr++;
                        break;
              case 'G':	while (argOrderOffset >= argOrderSize)
                        {  argOrderSize *= 2;
                           argOrderVector = (char *)NXZoneRealloc([self zone], argOrderVector, argOrderSize);
                        }
                        argOrderVector[argOrderOffset++] = 'G';
                        *sprintfControlStringPtr++ = 'f'; controlStringPtr++;
                        break;
              case 'B':	while (argOrderOffset >= argOrderSize)
                        {  argOrderSize *= 2;
                           argOrderVector = (char *)NXZoneRealloc([self zone], argOrderVector, argOrderSize);
                        }
                        argOrderVector[argOrderOffset++] = 'B'; *sprintfControlStringPtr++ = 'f';
                        controlStringPtr++;
                        break;
              case '%':	sprintfControlStringPtr++; controlStringPtr++;
                        break;
               default:	NXLogError("WWSliderCell: unknown specifier <%c> in controlString.\n", *controlStringPtr);
                        sprintfControlStringPtr++; controlStringPtr++;
			break;
           }
       }
     }
     else
     {  sprintfControlStringPtr++;  controlStringPtr++;
     }
  }
  argOrderVector[argOrderOffset] = 0;

  done = NO;

  if (!done && !strcmp("d", argOrderVector))
  {  [self resizeTclCommandForArgVector:argOrderVector];
     sprintf(tclCommand, sprintfControlString, [self intValue]);
     done = YES;
  }
  if (!done && !strcmp("f", argOrderVector))
  {  [self resizeTclCommandForArgVector:argOrderVector];
     sprintf(tclCommand, sprintfControlString, [self floatValue]);
     done = YES;
  }
  if (!done && !strcmp("s", argOrderVector))
  {  [self resizeTclCommandForArgVector:argOrderVector];
     sprintf(tclCommand, sprintfControlString, [self stringValue]);
     done = YES;
  }
  // for back compatibility
  if (!done && !strcmp("fff", argOrderVector))
  {  [self resizeTclCommandForArgVector:argOrderVector];
     sprintf(tclCommand, sprintfControlString, NXRedComponent(color), NXGreenComponent(color), NXBlueComponent(color));
     done = YES;
  }
  if (!done && !strcmp("RGB", argOrderVector))
  {  [self resizeTclCommandForArgVector:argOrderVector];
     sprintf(tclCommand, sprintfControlString, NXRedComponent(color), NXGreenComponent(color), NXBlueComponent(color));
     done = YES;
  }
  if (!done && !strcmp("fRGB", argOrderVector))
  {  [self resizeTclCommandForArgVector:argOrderVector];
     sprintf(tclCommand, sprintfControlString, [self floatValue], NXRedComponent(color), NXGreenComponent(color), NXBlueComponent(color));
     done = YES;
  }
  if (!done && !strcmp("RGBf", argOrderVector))
  {  [self resizeTclCommandForArgVector:argOrderVector];
     sprintf(tclCommand, sprintfControlString, NXRedComponent(color), NXGreenComponent(color), NXBlueComponent(color), [self floatValue]);
     done = YES;
  }
  if (!done && !strcmp("dRGB", argOrderVector))
  {  [self resizeTclCommandForArgVector:argOrderVector];
     sprintf(tclCommand, sprintfControlString, [self intValue], NXRedComponent(color), NXGreenComponent(color), NXBlueComponent(color));
     done = YES;
  }
  if (!done && !strcmp("RGBd", argOrderVector))
  {  [self resizeTclCommandForArgVector:argOrderVector];
     sprintf(tclCommand, sprintfControlString, NXRedComponent(color), NXGreenComponent(color), NXBlueComponent(color), [self intValue]);
     done = YES;
  }

  if (!done)
  {  while (tclCommandSize < controlStringSize)
     {  if (!tclCommandSize)
	{  tclCommandSize = 32;
           tclCommand = (char *)NXZoneCalloc([self zone], tclCommandSize, sizeof(char));
        }
        else
        {  tclCommandSize *= 2;
           tclCommand = (char *)NXZoneRealloc([self zone], tclCommand, tclCommandSize);
        }
     }
     strcpy(tclCommand, controlString);
  }
  tclCommandDirty = NO;

  NXZoneFree([self zone], argOrderVector);

  return self;
}


- evaluateSelf
{
   int   ret;
   char  *newStringValue;
   id    retID;


  newStringValue = [interp globalEval:tclExpression :&ret];
  if ((ret == TCL_OK) || (ret == TCL_RETURN))
  {  [self setStringValue:newStringValue];
     retID = self;
  }
  else
  {  NXLogError("WWSliderCell: error <%s> evaluating tcl expression <%s>\n", 
		newStringValue, tclExpression);
     if (newStringValue) { free(newStringValue); }
     retID = nil;
  }
  return retID;
}

- updateInterp
{
  if ([interp eval:[self tclCommand]])
  {  return self;
  }
  return nil;
}


- setControlString:(const char *)str 
{ 
  int   cnt;


  if (!str) { return self; }
  if (controlStringSize <= 0) 
  {  controlStringSize = 32; 
     controlString = (char *)NXZoneCalloc([self zone], controlStringSize, sizeof(char));
  }
  cnt = strlen(str);
  while (cnt >= controlStringSize)
  {  controlStringSize *= 2;
     controlString = (char *)NXZoneRealloc([self zone], controlString, controlStringSize);
  }
  if (sprintfControlString)
  {  sprintfControlString = (char *)NXZoneRealloc([self zone], sprintfControlString, controlStringSize);
  }
  else
  {  sprintfControlString = (char *)NXZoneCalloc([self zone], controlStringSize, sizeof(char));
  }
  strcpy(controlString, str); 
  tclCommandDirty = YES;
  return self; 
}
//
- (const char *)controlString { return controlString; }

- setTclExpression:(const char *)str 
{ 
  int   cnt;


  if (!str) { return self; }
  if (tclExpressionSize <= 0) 
  {  tclExpressionSize = 32; 
     tclExpression = (char *)NXZoneCalloc([self zone], tclExpressionSize, sizeof(char));
  }
  cnt = strlen(str);
  while (cnt >= tclExpressionSize)
  {  tclExpressionSize *= 2;
     tclExpression = (char *)NXZoneRealloc([self zone], tclExpression, tclExpressionSize);
  }
  strcpy(tclExpression, str); 
  return self; 
}
//
- (const char *)tclExpression { return tclExpression; }

- setTclVar:(const char *)str 
{ 
  int   cnt;


  if (!str) { return self; }
  if (tclVarSize <= 0) 
  {  tclVarSize = 32; 
     tclVar = (char *)NXZoneCalloc([self zone], tclVarSize, sizeof(char));
  }
  cnt = strlen(str);
  while (cnt >= tclVarSize)
  {  tclVarSize *= 2;
     tclVar = (char *)NXZoneRealloc([self zone], tclVar, tclVarSize);
  }
  strcpy(tclVar, str); 
  return self; 
}
//
- (const char *)tclVar { return tclVar; }

- setTclCommand:(const char *)str 
{ 
  int   cnt;


  if (!str) { return self; }
  if (tclCommandSize <= 0) 
  {  tclCommandSize = 32; 
     tclCommand = (char *)NXZoneCalloc([self zone], tclCommandSize, sizeof(char));
  }
  cnt = strlen(str);
  while (cnt >= tclCommandSize)
  {  tclCommandSize *= 2;
     tclCommand = (char *)NXZoneRealloc([self zone], tclCommand, tclCommandSize);
  }
  strcpy(tclCommand, str); 
  return self; 
}
//
- (const char *)tclCommand 
{ 
  if (tclCommandDirty)
  {  [self updateTclCommand];
  }
  return tclCommand; 
}


- write:(NXTypedStream *)stream 
{
   [super write:stream];
   NXWriteType(stream, "*", &controlString);
   NXWriteType(stream, "*", &tclVar);
   NXWriteType(stream, "*", &tclExpression);
   NXWriteType(stream, "*", &tclCommand);
   NXWriteObjectReference(stream, interp);
   return self;
}
//
- read:(NXTypedStream *)stream 
{
   int version;

   [super read:stream];

   version = NXTypedStreamClassVersion(stream, "WWThumbWheelCell");
   if (version == 1)
   {  NXReadType(stream, "*", &controlString);
      if (controlString) 
      {  controlStringSize = strlen(controlString) + 1;
         sprintfControlString = (char *)NXZoneCalloc([self zone], controlStringSize, sizeof(char));
      }
      else
      {  controlStringSize = 0;
         sprintfControlString = NULL;
      }
      NXReadType(stream, "*", &tclVar);
      if (tclVar) 
      {  tclVarSize = strlen(tclVar) + 1;
      }
      else
      { tclVarSize = 0;
      }
      tclCommandSize = 0;
      tclCommand = NULL;
   }
   if (version == 2)
   {  NXReadType(stream, "*", &controlString);
      if (controlString) 
      {  controlStringSize = strlen(controlString) + 1;
         sprintfControlString = (char *)NXZoneCalloc([self zone], controlStringSize, sizeof(char));
      }
      else
      {  controlStringSize = 0;
         sprintfControlString = NULL;
      }
      NXReadType(stream, "*", &tclVar);
      if (tclVar) 
      {  tclVarSize = strlen(tclVar) + 1;
      }
      else
      { tclVarSize = 0;
      }
      NXReadType(stream, "*", &tclExpression);
      if (tclExpression) 
      {  tclExpressionSize = strlen(tclExpression) + 1;
      }
      else
      {  tclExpressionSize = 0;
      }
      tclCommandSize = 0;
      tclCommand = NULL;
   }
   if (version == 3)
   {  NXReadType(stream, "*", &controlString);
      if (controlString) 
      {  controlStringSize = strlen(controlString) + 1;
      }
      else
      {  controlStringSize = 0;
      }
      NXReadType(stream, "*", &tclVar);
      if (tclVar) 
      {  tclVarSize = strlen(tclVar) + 1;
      }
      else
      { tclVarSize = 0;
      }
      NXReadType(stream, "*", &tclExpression);
      if (tclExpression) 
      {  tclExpressionSize = strlen(tclExpression) + 1;
      }
      else
      {  tclExpressionSize = 0;
      }
      NXReadType(stream, "*", &tclCommand);
      if (tclCommand) 
      {  tclCommandSize = strlen(tclCommand) + 1;
      }
      else
      { tclCommandSize = 0;
      }
      NXReadType(stream, "*", &sprintfControlString);
   }
   if (version == 4)
   {  NXReadType(stream, "*", &controlString);
      if (controlString) 
      {  controlStringSize = strlen(controlString) + 1;
      }
      else
      {  controlStringSize = 0;
      }
      NXReadType(stream, "*", &tclVar);
      if (tclVar) 
      {  tclVarSize = strlen(tclVar) + 1;
      }
      else
      { tclVarSize = 0;
      }
      NXReadType(stream, "*", &tclExpression);
      if (tclExpression) 
      {  tclExpressionSize = strlen(tclExpression) + 1;
      }
      else
      {  tclExpressionSize = 0;
      }
      NXReadType(stream, "*", &tclCommand);
      if (tclCommand) 
      {  tclCommandSize = strlen(tclCommand) + 1;
      }
      else
      { tclCommandSize = 0;
      }
      NXReadType(stream, "*", &sprintfControlString);
      interp = NXReadObject(stream);
      if (sprintfControlString) {  free(sprintfControlString); }
      if (controlStringSize)
      {  sprintfControlString = (char *)NXZoneCalloc([self zone], controlStringSize, sizeof(char));
      }
      else
      {  sprintfControlString = NULL;
      }
   }
   if (version == 5)
   {  NXReadType(stream, "*", &controlString);
      if (controlString) 
      {  controlStringSize = strlen(controlString) + 1;
         sprintfControlString = (char *)NXZoneCalloc([self zone], controlStringSize, sizeof(char));
      }
      else
      {  controlStringSize = 0;
         sprintfControlString = NULL;
      }
      NXReadType(stream, "*", &tclVar);
      if (tclVar) 
      {  tclVarSize = strlen(tclVar) + 1;
      }
      else
      { tclVarSize = 0;
      }
      NXReadType(stream, "*", &tclExpression);
      if (tclExpression) 
      {  tclExpressionSize = strlen(tclExpression) + 1;
      }
      else
      {  tclExpressionSize = 0;
      }
      NXReadType(stream, "*", &tclCommand);
      if (tclCommand) 
      {  tclCommandSize = strlen(tclCommand) + 1;
      }
      else
      { tclCommandSize = 0;
      }
      interp = NXReadObject(stream);
   }
   return self;
}

// IB stuff
- (const char *)getInspectorClassName 
{  
   NXEvent  *event = [NXApp currentEvent];

   if (event->flags & NX_ALTERNATEMASK)
   {  return [super getInspectorClassName];
   }
   
   return "WWThumbWheelCellIBInspector"; 
}

@end
